export {}
// 1) 泛型约束 : 对类型参数作一定的限制
// 现在泛型 T 必须包含属性name；entities 的每一项可以是一个对象，必须含有类型为string的name属性。
function getCnames<T extends { name: string }>(entities: T[]): string[] {
  return entities.map((entity) => entity.name);
}

// 2) 条件类型与高阶类型 
// extends还有一大用途就是用来判断一个类型是不是可以分配给另一个类型,在写高级类型的时候非常有用
  type Human = {
    name: string;
    age: number;
  }
  type Duck = {
    name: string;
  }
  type Bool = Duck extends Human ? 'yes' : 'no'; 

// 3) 分配条件类型
// extends前面的参数是一个泛型类型，当传入该参数的是联合类型，则使用分配律计算最终的结果。
// 分配律是指，将联合类型的联合项拆成单项，分别代入条件类型，然后将每个单项代入得到的结果再联合起来，得到最终的判断结果。
type A1 = 'x' extends 'x' ? string : number; // string
type A2 = 'y' extends 'x' ? string : number; // number

/* P是带参数T的泛型类型, extends的前参为T，T是一个泛型参数。
在A3的定义中，给T传入的是'x'和'y'的联合类型'x' | 'y'，满足分配律，于是'x'和'y'被拆开，分别代入
P<'x' | 'y'> => P<'x'> | P<'y'>
    'x' extends 'x' ? string : number => string
    'y' extends 'x' ? string : number => number
    将每一项代入得到的结果联合起来，得到string | number
满足两个要点即可适用分配律：第一，参数是泛型类型，第二，代入参数的是联合类型
*/
type P<T> = T extends 'x' ? string : number;
type A3 = P<'x' | 'y'>; // string | number

/* 特殊的never，never是所有类型的子类型 */
type A4 = never extends 'x' ? string : number; // string
// never被认为是空的联合类型,没有联合项的联合类型。
// A5满足上面的分配律，然而因为没有联合项可以分配，P<T>的表达式根本没有执行，A5的定义类似于永远没有返回的函数一样，结果是never类型。
type A5 = P<never> // never

// 4）防止条件判断中的分配
/* 在条件判断类型的定义中，将泛型参数使用[]括起来，即可阻断条件判断类型的分配，
此时，传入参数T的类型将被当做一个整体，不再分配。 
*/
type Q<T> = [T] extends ['x'] ? string : number;
type A6 = Q<'x' | 'y'> // number
type A7 = Q<never> // string

// Exclude泛型就是使用extends的条件类型 分配原则
type MyExclude<T, U> = T extends U ? never : T
type MyExcludeA1_0 = MyExclude<'key1' | 'key2', 'key2'> // 'key1'
// 推导出
type MyExcludeA1_1 = MyExclude<'key1', 'key2'> | MyExclude<'key2', 'key2'>
// 推导出
type MyExcludeA1_2 = ('key1' extends 'key2' ? never : 'key1') | ('key2' extends 'key2' ? never : 'key2');
// 推导出
type MyExcludeA1_3 = 'key1' | (never);

// Extract泛型，是提取前一个类型被包括在后一个类型中。使用extends推导过程
type MyExtract<T, U> = T extends U ? T : never;
type MyExtractA1_0 = MyExtract<'key1' | 'key2', 'key2'> //'key2'
// 推导出
type MyExtractA1_1 = MyExtract<'key1', 'key2'> | MyExtract<'key2', 'key2'>;
// 推导出
type MyExtractA1_2 = ('key1' extends 'key2' ? 'key1' : never) | ('key2' extends 'key2' ? 'key2' : never);
// 推导出
type MyExtractA1_3 = 'key2' | never;

// extends的条件判断，除了定义条件类型，还能在泛型表达式中用来约束泛型参数
// Pick<T, K extends keyof T> : Pick从接口T中，将联合类型K中的项挑选出来，形成一个新的接口
// K extends keyof T  是用来约束K的条件，传入K的参数必须使得这个条件为真，否则ts就会报错，
// 也就是说，K的联合项必须来自接口T的属性。
type MyPick<T, K extends keyof T> = {
    [P in K] : T[P]
}
interface A {
    name: string;
    age: number;
    sex: number;
}
type MyPickA1 = MyPick<A, 'name'|'age'> // {name: string; age: number;}
// 报错：类型“"key" | "noSuchKey"”不满足约束“keyof A”
// type MyPickA2 = MyPick<A, 'name'|'noSuchKey'>